package com.gitee.wsl.jvm.task.thread.executor

import com.gitee.wsl.jvm.task.thread.Scheduler
import com.gitee.wsl.jvm.task.thread.Wrapper
import timber.log.Timber
import java.util.LinkedList
import java.util.concurrent.Executor

/**
 * Support control the currency. <br></br>
 * Support change the execute window in range [minSize, maxSize]. <br></br>
 * Support change priority by state(pause/resume) of UI component.
 */
class PipeExecutor : Executor {
    private val frontList = LinkedList<Runnable>()
    private val backList = LinkedList<Runnable>()
    private val minSize: Int
    private val maxSize: Int
    private var windowSize: Int
    private var count = 0
    private val executor = Scheduler.executor

    constructor(windowSize: Int) {
        this.windowSize = if (windowSize > 0) windowSize else 1
        minSize = this.windowSize
        maxSize = this.windowSize
    }

    constructor(minSize: Int, maxSize: Int) {
        this.minSize = if (minSize > 0) minSize else 1
        windowSize = this.minSize
        this.maxSize = this.minSize.coerceAtLeast(maxSize)
    }

    @Synchronized
    override fun execute(r: Runnable) {
        if (count < windowSize) {
            start(r)
        } else {
            frontList.offer(r)
        }
    }

    private fun start(r: Runnable) {
        count++
        executor.execute {
            try {
                r.run()
            } catch (e: Throwable) {
                Timber.d( e)
            } finally {
                scheduleNext()
            }
        }
    }

    @Synchronized
    private fun scheduleNext() {
        count--
        if (count < windowSize) {
            val next = poll()
            if (next != null) {
                start(next)
            }
        }
    }

    private fun poll(): Runnable? {
        val r = frontList.poll()
        return r ?: backList.poll()
    }

    @Synchronized
    fun pushBack(r: Runnable) {
        flip(frontList, backList, r)
    }

    @Synchronized
    fun popFront(r: Runnable) {
        flip(backList, frontList, r)
    }

    private fun flip(aList: LinkedList<Runnable>, bList: LinkedList<Runnable>, r: Runnable) {
        if (aList.isEmpty()) return
        val it = aList.iterator()
        while (it.hasNext()) {
            val e = it.next()
            if ((e == r || e is Wrapper) && (e as Wrapper).task == r) {
                it.remove()
                bList.offer(e)
                break
            }
        }
    }

    @Synchronized
    fun extendWindow() {
        if (windowSize < maxSize) {
            windowSize++
            if (count < windowSize) {
                val r = poll()
                if (r != null) {
                    start(r)
                }
            }
        }
    }

    @Synchronized
    fun reduceWindow() {
        if (windowSize > minSize) {
            windowSize--
        }
    }
}